/*
 * Copyright 2011-2013, David George, Licensed under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.magneato.dto;

import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.UUID;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.lang3.StringEscapeUtils;
import org.magneato.mvc.MainController;
import org.magneato.service.Repository;
import org.magneato.utils.parsers.HashList;
import org.magneato.utils.parsers.HashListContentHandler;
import org.magneato.utils.parsers.WikiParser;
import org.xml.sax.SAXException;

@XmlRootElement
public class Page implements Serializable {
	/**
	 * increment this value every time you change class variables
	 */

	private static final long serialVersionUID = 1L;

	public final static String ROOT = "-";

	private transient Repository repository;
	private transient WikiParser wikiParser;
	private transient HashList rootNode;

	private String name;
	private String title;
	private String author;
	private String group;
	private long perms;
	private ArrayList<String> relations;
	private String content;

	public enum Status {
		DRAFT, REVIEW, PUBLISHED
	}

	private Status status;
	@XmlElement(name = "uuid", required = true)
	private final String uuid = UUID.randomUUID().toString();
	private long createDate;
	private long startDate;
	private long expiryDate;
	private String editTemplate;
	private String viewTemplate;
	private String ipAddr;

	public void setWikiParser(WikiParser wikiParser) {
		this.wikiParser = wikiParser;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) throws NullPointerException {
		if (title == null || title.isEmpty()) {
			throw new NullPointerException("Page title cannot be null or empty");
		}
		this.title = title;
	}

	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	public String getGroup() {
		return group;
	}

	public void setGroup(String group) {
		this.group = group;
	}

	public long getPerms() {
		return perms;
	}

	public void setPerms(long perms) {
		this.perms = perms;
	}

	public ArrayList<String> getRelations() {
		return relations;
	}

	public void setParent(String parent) {
		relations = new ArrayList<String>();
		relations.add(parent);
	}

	/**
	 * Parent is first relation
	 * 
	 * @return
	 */
	public String getParent() {
		if (relations != null) {
			return relations.get(0);
		}
		return null;
	}

	public synchronized void addRelation(String relation) {
		assert relations != null;
		relations.add(relation);
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}

	public String getEscapedContent() {
		return StringEscapeUtils.escapeXml(content);
	}

	public Status getStatus() {
		return status;
	}

	public void setStatus(Status status) {
		this.status = status;
	}

	public String getUuid() {
		return uuid;
	}

	public long getCreateDate() {
		return createDate;
	}

	public String dateString(String format) {
		if (format.isEmpty()) {
			format = "yyyy-MM-dd";
		}
		final DateFormat dateFormat = new SimpleDateFormat(format);

		String date = dateFormat.format(createDate);
		return date;
	}

	public void setCreateDate(long createDate) {
		this.createDate = createDate;
	}

	public String getEditTemplate() {
		return editTemplate;
	}

	public void setEditTemplate(String editTemplate) {
		this.editTemplate = editTemplate;
	}

	public String getViewTemplate() {
		return viewTemplate;
	}

	public void setViewTemplate(String viewTemplate) {
		this.viewTemplate = viewTemplate;
	}

	public String getIpAddr() {
		return ipAddr;
	}

	public void setIpAddr(String ipAddr) {
		this.ipAddr = ipAddr;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public long getStartDate() {
		return startDate;
	}

	public void setStartDate(long startDate) {
		this.startDate = startDate;
	}

	public long getExpiryDate() {
		return expiryDate;
	}

	public void setExpiryDate(long expiryDate) {
		this.expiryDate = expiryDate;
	}

	@Override
	public String toString() {
		return "Page [name=" + name + ", author=" + author + ", group=" + group
				+ ", perms=" + perms + ", relations=" + relations
				+ ", content=" + content + ", status=" + status + ", uuid="
				+ uuid + ", createDate=" + createDate + ", editTemplate="
				+ editTemplate + ", viewTemplate=" + viewTemplate + ", ipAddr="
				+ ipAddr + "]";
	}

	public String formatNumber(double d, String format) {
		NumberFormat formatter = new DecimalFormat(format);
		return formatter.format(d);
	}

	public String getNameSpace() {
		return getNameSpace(this.name);
	}

	/**
	 * @uri Uniform Resource Indicator Of the form: /<pagename>.htm or
	 *      /<namespace>/<pagename>.htm or
	 *      <http://hostname>/<namespace>/<pagename>.htm
	 * 
	 *      If no namespace is specified we return the string "-"
	 * 
	 * @return just the page name component stripped of initial path and .htm or
	 *         null if no namespace
	 */
	public static String getNameSpace(String uri) {
		if (uri != null) {
			if (uri.startsWith("http:")) {
				try {
					URL url = new URL(uri);
					uri = url.getPath();
				} catch (MalformedURLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			int i = uri.lastIndexOf('/');
			if (i == 0) {
				// no namespace, this is the root
				return ROOT;
			} else if (i < 0) {
				// format error
				return null;
			}
			return uri.substring(1, i);
		}

		return null;
	}

	public String getShortName() {
		return getShortName(this.name);
	}

	/**
	 * Strip off name space portion of uri
	 * 
	 * @param uri
	 * @return
	 */
	public static String getShortName(String uri) {
		int i = uri.lastIndexOf('/');
		if (uri.endsWith(".htm")) {
			return uri.substring(i + 1, uri.length() - 4);
		} else {
			return uri.substring(i + 1);
		}
	}

	/* Search Utilities */
	public void setRepository(Repository repository) {
		this.repository = repository;
	}

	public SearchResults getChildren(int start, int end) {
		if (repository != null) {
			return repository.search("relation:" + getUuid(), start, end);
		}
		return null;

	}

	public SearchResults search(String search, int start, int end) {
		if (repository != null) {
			return repository.search(search, start, end);
		}
		return null;
	}

	public Result getParentName() {
		if (repository != null) {
			SearchResults searchResults = repository.search(
					"uuid:" + this.getParent(), 0, 1);
			Iterator<Result> i = searchResults.getResults();
			if (i.hasNext()) {
				return i.next();
			}
		}
		return null;
	}

	public static String snippet(String text, int size) {
		if (text.length() < size) {
			return text;
		} else {
			int i = text.indexOf('.', size);
			if (i > size) {
				return text.substring(0, i + 1);
			} else {
				return text.substring(0, size);
			}
		}
	}

	/**
	 * Gets page contents and meta data but don't parse contents yet
	 * 
	 * @param uri
	 * @return A page object, can be null if page not found
	 */
	public Page getPage(String uri) {
		if (uri != null) {
			Page page = repository.get(uri);
			page.setWikiParser(wikiParser);
			return page;
		}
		return null;
	}

	/**
	 * Load's a page lazily based on the page's name. Don't parse contents until
	 * later, maybe we just want meta data at this stage
	 * 
	 * @return Page or null if name not found in repository
	 */
	public Page getPage() {
		if (name != null) {
			Page page = repository.get(name);
			page.setWikiParser(wikiParser);
			return page;
		}
		return null;
	}

	/**
	 * Parses the page contents into a HashList
	 * 
	 * @throws SAXException
	 * @throws ParserConfigurationException
	 */
	public HashList getRootNode() throws ParserConfigurationException,
			SAXException {
		if (rootNode == null) {
			HashListContentHandler myHandler = MainController.parseContents(
					content, wikiParser);
			rootNode = myHandler.getRootNode();
		}
		return rootNode;
	}

}